<?php
/** 
 * EGP Framework
 *
 * LICENSE
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @author     Akon(番茄红了) <aultoale@gmail.com>
 * @copyright  Copyright (c) 2008 (http://www.tblog.com.cn)
 * @license    http://www.gnu.org/licenses/gpl.html     GPL 3
 */

!defined('EGP_LIB_DIR') && die('Access Deny!');

/**
 * View 视图处理
 *
 * @package    classes
 * @author     Akon(番茄红了) <aultoale@gmail.com>
 * @copyright  Copyright (c) 2008 (http://www.tblog.com.cn)
 * @license    http://www.gnu.org/licenses/gpl.html     GPL 3
 */
class EGP_View
{

    /**
     * EGP_Controller_Action
     *
     * @var EGP_Controller_Action
     */
    private $_controller = null;

    /**
     * View 文件扩展名
     *
     * @var string
     */
    private $_suffix = '.php';

    /**
     * View 文件所在路径
     *
     * @var string
     */
    private $_scriptPath = null;

    /**
     * Helper 所在路径
     *
     * @var array
     */
    private $_helperPath = array();

    /**
     * 存储已载入的 View Helper 的实例
     *
     * @var array
     */
    private $_loadedHelper = array();

    /**
     * 值为 TRUE 时永远不执行 render 操作
     *
     * @var boolean
     */
    public $noRender = false;

    /**
     * 构造方法
     *
     * @param EGP_Controller_Action $controller
     */
    public function __construct(EGP_Controller_Action $controller)
    {
        $this->_controller = $controller;
        $this->addHelperPath(EGP_HLP_DIR);
    }

    /**
     * 获取 EGP_Controller_Action
     *
     * @return EGP_Controller_Action
     */
    public function getController()
    {
        return $this->_controller;
    }

    /**
     * 设置 View 文件扩展名
     *
     * @param  string  $suffix
     * @return EGP_View
     * @throws EGP_Exception
     */
    public function setSuffix($suffix = '.php')
    {
        if (!preg_match('/^\.\w+$/', $suffix))
            throw new EGP_Exception("非法的 View 扩展名：'$suffix'");
        $this->_suffix = $suffix;
        return $this;
    }

    /**
     * 获取 View 文件扩展名
     *
     * @return string
     */
    public function getSuffix()
    {
        return $this->_suffix;
    }

    /**
     * 设置 View 文件所在路径
     *
     * @param  string  $path
     * @return EGP_View
     * @throws EGP_Exception
     */
    public function setScriptPath($path)
    {
        if (!is_dir($path))
            throw new EGP_Exception("无效的 View Script 路径：$path");
        $this->_scriptPath = $path;
        return $this;
    }

    /**
     * 获取 View 文件所在路径
     *
     * @return string
     */
    public function getScriptPath()
    {
        return $this->_scriptPath;
    }

    /**
     * 添加 Helper 文件查找路径
     *
     * @param  string  $path
     * @return EGP_View
     * @throws EGP_Exception
     */
    public function addHelperPath($path)
    {
        if (!is_dir($path))
            throw new EGP_Exception("无效的 View Helper 路径：$path");
        $this->_helperPath[] = $path;
        return $this;
    }

    /**
     * 获取所有 Hepler 文件查找路径
     *
     * @return array
     */
    public function getHelperPaths()
    {
        return $this->_helperPath;
    }

    /**
     * 指派变量
     *
     * @param  mixed  $key
     * @param  mixed  $value
     * @return EGP_View
     * @throws EGP_Exception
     */
    public function assign($key, $value = null)
    {
        if (is_string($key)) {
            if ('_' != substr($key, 0, 1)) {
                $this->$key = $value;
                return;
            }
            throw new EGP_Exception('禁止设置 private 或者 protected 成员属性');
        } elseif (is_array($key)) {
            foreach ($key as $k => $v)
                $this->assign($k, $v);
        } else {
            throw new EGP_Exception("assign() 的参数必须是一个字符串或者数组");
        }
        return $this;
    }

    /**
     * 获取已指派的变量
     *
     * @param  string  $key
     * @return mixed
     */
    public function getVar($key)
    {
        return $this->__isset($key) ? $this->$key : null;
    }

    /**
     * 获取所有已指派的变量
     *
     * @return array
     */
    public function getVars()
    {
        $vars = get_object_vars($this);
        foreach ($vars as $key => $value) {
            if ('_' == substr($key, 0, 1))
                unset($vars[$key]);
        }
        return $vars;
    }

    /**
     * 清除所有已指派的变量
     *
     * @return EGP_View
     */
    public function cleanVars()
    {
        $vars = get_object_vars($this);
        foreach ($vars as $key => $value) {
            if ('_' != substr($key, 0, 1))
                unset($this->$key);
        }
    }

    /**
     * 通过魔术方法指派变量
     *
     * @param  mixed  $key
     * @param  mixed  $value
     */
    public function __set($key, $value)
    {
        return $this->assign($key, $value);
    }

    /**
     * 通过魔术方法获取已指派的变量
     *
     * @param  string  $key
     * @return mixed
     */
    public function __get($key)
    {
        return null;
    }

    /**
     * 通过魔术方法判断变量是否已指派
     *
     * @param  string  $key
     * @return boolean
     */
    public function __isset($key)
    {
        return ('_' != substr($key, 0, 1)) ? isset($this->$key) : false;
    }

    /**
     * 通过魔术方法删除一个已指派的变量
     *
     * @param  string  $key
     */
    public function __unset($key)
    {
        if ('_' != substr($key, 0, 1) && isset($this->$key))
            unset($this->$key);
    }

    /**
     * 当调用不存在的方法时自动调用 View Helper
     *
     * @param  string  $method
     * @param  array   $args
     * @return mixed
     */
    public function __call($method, $args)
    {
        return $this->callHelper($method, $args);
    }

    /**
     * 载入并实例化 View Helper
     *
     * @param  stirng  $helper
     * @return EGP_View_Helper_Abstract
     * @throws EGP_Exception
     */
    public function loadHelper($helper)
    {
        if (!isset($this->_loadedHelper[$helper])) {
            $file = str_replace('_', DIR_SEP, ucfirst($helper)) . '.php';
            $class = 'EGP_View_Helper_' . ucfirst($helper);
            if (!class_exists($class, false)) {
                $file = EGP_Core::findFile($file, $this->getHelperPaths());
                if ($file !== false)
                    require($file);
            }
            if (!class_exists($class, false))
                throw new EGP_Exception("$class 不存在或者无法加载");
            $helperObj = new $class($this);
            if (!$helperObj instanceof EGP_View_Helper_Abstract)
                throw new EGP_Exception("$class 必须从 EGP_View_Helper_Abstract 继承");
            $this->_loadedHelper[$helper] = $helperObj;
        }
        return $this->_loadedHelper[$helper];
    }

    /**
     * 调用 View Helper
     *
     * @param  string  $helper
     * @param  array   $args
     * @return mixed
     */
    public function callHelper($helper, $args)
    {
        $helperObj = $this->loadHelper($helper);
        return call_user_func_array(array(&$helperObj, 'init'), $args);
    }

    /**
     * 判断脚本页面是否存在
     *
     * @param  string  $script
     * @return string|false
     */
    public function exists($script)
    {
        return is_readable($script) ? $script : false;
    }

    /**
     * 加载 VIEW 页面
     *
     * @param  string  $filename
     * @return EGP_View
     * @throws EGP_Exception
     */
    public function render($script)
    {
        if ($this->noRender !== true) {
            $filename = $this->getScriptPath() . DIR_SEP . $script . $this->getSuffix();
            if (!$this->exists($filename))
                throw new EGP_Exception("文件 '$filename' 不存在或者无法加载");
            extract($this->getVars(), EXTR_REFS);
            require($filename);
        }
        return $this;
    }

}